/*
 * ResourceUtil.java
 */

package xq.util;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import xq.util.resource.BinaryResource;
import xq.util.resource.TextResource;

import static xq.util.CommonUtil.empty;
import static xq.util.CommonUtil.substringAfter;
import static xq.util.IOUtil.readText;

/**
 * Provides general purpose utility operations for reading classpath resources.
 * 
 * @author ThorntonRP
 */
public final class ResourceUtil {

	/**
	 * 
	 * @param resourceFilter
	 * @return
	 * @throws java.io.IOException
	 */
	public static String[] getResourceNames(FileFilter resourceFilter) throws IOException {
		return getResourceNames(resourceFilter, getDefaultLoader());
	}

	/**
	 * 
	 * @param resourceFilter
	 * @param resourceLoader
	 * @return
	 * @throws java.io.IOException
	 */
	public static String[] getResourceNames(FileFilter resourceFilter, ClassLoader resourceLoader) throws IOException {
		List<String> results = Collections.emptyList();
		File[] roots = ResourceUtil.getResourceFiles(".", resourceLoader);
		if (roots != null) {
			results = new ArrayList<String>();
			for (File root : roots) {
				getResourceNames(root.getPath() + File.separator, root, resourceFilter, results);
			}
		}
		return results.toArray(new String[results.size()]);
	}

	/*--------------------------------------------------------------------*/

	/**
	 * 
	 * @param resourcePath
	 * @return
	 * @throws java.io.IOException
	 */
	public static TextResource getTextResource(String resourcePath) throws IOException {
		return getTextResource(resourcePath, getDefaultLoader());
	}

	/**
	 * 
	 * @param resourcePath
	 * @param resourceLoader
	 * @return
	 * @throws IOException
	 */
	public static TextResource getTextResource(String resourcePath, ClassLoader resourceLoader) throws IOException {
		URL url = getResourceURL(resourcePath, resourceLoader);
		return url == null ? null : new TextResource(url, null);
	}

	/*--------------------------------------------------------------------*/

	/**
	 * 
	 * @param resourcePath
	 * @return
	 * @throws IOException
	 */
	public static BinaryResource getBinaryResource(String resourcePath) throws IOException {
		return getBinaryResource(resourcePath, getDefaultLoader());
	}

	/**
	 * 
	 * @param resourcePath
	 * @param resourceLoader
	 * @return
	 * @throws IOException
	 */
	public static BinaryResource getBinaryResource(String resourcePath, ClassLoader resourceLoader) throws IOException {
		URL url = getResourceURL(resourcePath, resourceLoader);
		return url == null ? null : new BinaryResource(url, null);
	}

	/*--------------------------------------------------------------------*/

	/**
	 * Returns a URL for the resource at the given path in the current context
	 * class loader.
	 * @param resourcePath The location of the resource
	 * @return
	 */
	public static URL getResourceURL(String resourcePath) {
		return getDefaultLoader().getResource(resourcePath);
	}

	/**
	 * Returns a URL for the resource at the given path in the specified class
	 * loader.
	 * @param resourcePath The location of the resource
	 * @param resourceLoader 
	 * @return
	 */
	public static URL getResourceURL(String resourcePath, ClassLoader resourceLoader) {
		return resourceLoader.getResource(resourcePath);
	}

	/*--------------------------------------------------------------------*/

	/**
	 * Returns all URLs for resources at the given path in the current context
	 * class loader.
	 * @param resourcePath The location of the resources
	 * @return
	 * @throws IOException 
	 */
	public static URL[] getResourceURLs(String resourcePath) throws IOException {
		return getResourceURLs(resourcePath, getDefaultLoader());
	}

	/**
	 * Returns all URLs for resources at the given path in the specified class
	 * loader.
	 * @param resourcePath The location of the resources
	 * @param resourceLoader 
	 * @return
	 * @throws IOException 
	 */
	public static URL[] getResourceURLs(String resourcePath, ClassLoader resourceLoader) throws IOException {
		List<URL> resourceURLs = Collections.emptyList();
		Enumeration<URL> e = resourceLoader.getResources(resourcePath);
		if (e.hasMoreElements()) {
			resourceURLs = new ArrayList<URL>();
			while (e.hasMoreElements()) {
				resourceURLs.add(e.nextElement());
			}
		}
		return resourceURLs.toArray(new URL[resourceURLs.size()]);
	}

	/*--------------------------------------------------------------------*/

	/**
	 * Returns a file to the resource at the given path in the current context
	 * class loader.
	 * @param resourcePath The location of the resource
	 * @return
	 * @throws IOException 
	 */
	public static File getResourceFile(String resourcePath) throws IOException {
		return getResourceFile(resourcePath, getDefaultLoader());
	}

	/**
	 * Returns a file to the resource at the given path in the specified class
	 * loader.
	 * @param resourcePath The location of the resource
	 * @param resourceLoader 
	 * @return
	 * @throws IOException 
	 */
	public static File getResourceFile(String resourcePath, ClassLoader resourceLoader) throws IOException {
		URL resourceURL = getResourceURL(resourcePath, resourceLoader);
		if (resourceURL != null && "file".equals(resourceURL.getProtocol())) {
			String fileName = URLDecoder.decode(resourceURL.getFile(), "UTF-8");
			return new File(fileName);
		} else {
			return null;
		}
	}

	/*--------------------------------------------------------------------*/

	/**
	 * Returns all files to resources at the given path in the current context
	 * class loader.
	 * @param resourcePath The location of the resources
	 * @return
	 * @throws IOException 
	 */
	public static File[] getResourceFiles(String resourcePath) throws IOException {
		return getResourceFiles(resourcePath, getDefaultLoader());
	}

	/**
	 * Returns all files to resources at the given path in the specified class
	 * loader.
	 * @param resourcePath The location of the resources
	 * @param resourceLoader 
	 * @return
	 * @throws IOException 
	 */
	public static File[] getResourceFiles(String resourcePath, ClassLoader resourceLoader) throws IOException {
		List<File> resourceFiles = Collections.emptyList();
		URL[] resourceURLs = getResourceURLs(resourcePath, resourceLoader);
		if (resourceURLs != null) {
			resourceFiles = new ArrayList<File>(resourceURLs.length);
			for (URL resourceURL : resourceURLs) {
				if (resourceURL != null && "file".equals(resourceURL.getProtocol())) {
					String fileName = URLDecoder.decode(resourceURL.getFile(), "UTF-8");
					resourceFiles.add(new File(fileName));
				}
			}
		}
		return resourceFiles.toArray(new File[resourceFiles.size()]);
	}

	/*--------------------------------------------------------------------*/

	/**
	 * Returns an input stream for reading the supplied resource.
	 * 
	 * @param resourcePath The location of the resource
	 * @return An input stream
	 * @throws IOException if there is an I/O error while obtaining an input
	 *         stream.
	 */
	public static InputStream getResourceStream(String resourcePath) throws IOException {
		return getResourceStream(resourcePath, getDefaultLoader());
	}

	/**
	 * Returns an input stream for reading the supplied resource.
	 * 
	 * @param resourcePath The location of the resource
	 * @param resourceLoader 
	 * @return An input stream
	 * @throws IOException if there is an I/O error while obtaining an input
	 *         stream.
	 */
	public static InputStream getResourceStream(String resourcePath, ClassLoader resourceLoader) throws IOException {
		URL resourceURL = getResourceURL(resourcePath, resourceLoader);
		FileInputStream in = null;
		if (resourceURL != null) {
			String fileName = URLDecoder.decode(resourceURL.getFile(), "UTF-8");
			File resourceFile = new File(fileName);
			in = new FileInputStream(resourceFile);
		}
		return in;
	}

	/*--------------------------------------------------------------------*/

	/**
	 * Loads text content from the supplied resource path.
	 * 
	 * @param resourcePath The location of the resource
	 * @return The resource's text content
	 * @throws IOException if there is an I/O error while reading the resource.
	 */
	public static String getResourceText(String resourcePath) throws IOException {
		return getResourceText(resourcePath, getDefaultLoader());
	}

	/**
	 * Loads text content from the supplied resource path.
	 * 
	 * @param resourcePath The location of the resource
	 * @param resourceLoader 
	 * @return The resource's text content
	 * @throws IOException if there is an I/O error while reading the resource.
	 */
	public static String getResourceText(String resourcePath, ClassLoader resourceLoader) throws IOException {
		InputStream in = getResourceStream(resourcePath, resourceLoader);
		String resource = null;
		if (in != null) {
			resource = readText(in);
		}
		return resource;
	}

	/*--------------------------------------------------------------------
	 * Private Operations
	 *--------------------------------------------------------------------*/

	/**
	 * 
	 * @return
	 */
	private static ClassLoader getDefaultLoader() {
		return Thread.currentThread().getContextClassLoader();
	}
	
	/**
	 * 
	 * @param rootPath
	 * @param dir
	 * @param filter
	 * @param results
	 */
	private static void getResourceNames(String rootPath, File dir, FileFilter filter, List<String> results) {
		File[] resourceFiles = dir.listFiles(filter);
		if (!empty(resourceFiles)) {
			for (File resourceFile : resourceFiles) {
				if (resourceFile.isDirectory()) {
					getResourceNames(rootPath, resourceFile, filter, results);
				} else {
					String path = resourceFile.getPath();
					path = substringAfter(path, rootPath);
					path = path.replace('\\', '/');
					results.add(path);
				}
			}
		}
	}
	
	/**
	 * Default constructor, made private to prevent instantiation.
	 */
	private ResourceUtil() {
	}
}
